Skip to content

Conversation

@brittanycho
Copy link
Contributor

Cloud Run No Build Initial MVP Implementation

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @brittanycho, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers the foundational implementation for 'no-build' Cloud Run functions, streamlining the deployment of applications by allowing direct execution without an explicit build phase. It involves significant updates to the core deployment logic, including new runtime delegates, expanded configuration options for Cloud Run services, and direct integration with the Cloud Run V2 API. The changes also lay the groundwork for new language support, starting with experimental Dart runtime capabilities, and refine the source packaging process to accommodate these new deployment paradigms.

Highlights

  • Cloud Run No-Build Support: This PR introduces the initial MVP implementation for 'no-build' Cloud Run functions, allowing direct deployment of applications without a separate build step. This is enabled by a new runtime delegate that processes functions.yaml files.
  • Extended Configuration for Cloud Run: Internal data structures, such as the Endpoint and Container types, have been extended to support Cloud Run-specific configurations like baseImageUri, command, and args, which are crucial for defining no-build functions.
  • Cloud Run V2 API Integration: Full CRUD (Create, Read, Update, Delete) operations for Cloud Run services have been implemented by integrating with the Cloud Run V2 API, enabling the management of these new function types.
  • Experimental Dart Language Support: Experimental support for the Dart language and dart3 runtime has been added, demonstrated by the inclusion of a sample Dart HTTP server and corresponding functions.yaml configuration.
  • Source Packaging Adaptation: The source packaging mechanism has been adapted to use tar.gz archiving for no-build functions, optimizing the deployment process for these new Cloud Run services.
  • Trigger and IAM Restrictions: Cloud Run functions are now excluded from standard HTTP IAM checks, and specific trigger types (schedule, task queue, blocking) are currently not supported for the 'run' platform, with appropriate error handling implemented.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the initial MVP for "Cloud Run No Build" support in Firebase Functions. This allows deploying pre-built container images to Cloud Run. The changes span across the deployment pipeline, from configuration parsing (functions.yaml), source packaging (.tar.gz), to the deployment logic (fabricator.ts) which now interacts with the Cloud Run v2 API. New types and runtime delegates are added to support this feature. The changes look solid and cover the necessary aspects for this new deployment platform. I have a few suggestions to improve code quality, including removing duplicated code, fixing a minor bug, and cleaning up some temporary code artifacts like empty JSDoc comments and unnecessary type assertions.

Comment on lines +602 to +717
async createRunService(endpoint: backend.Endpoint): Promise<void> {
const storageSource = this.sources[endpoint.codebase!]?.storage;
if (!storageSource) {
logger.debug("Precondition failed. Cannot create a Cloud Run function without storage");
throw new Error("Precondition failed");
}
const service: Omit<runV2.Service, runV2.ServiceOutputFields> = {
name: `projects/${endpoint.project}/locations/${endpoint.region}/services/${endpoint.id}`,
template: {
containers: [
{
name: "worker",
image: "scratch",
command: endpoint.command,
args: endpoint.args,
baseImageUri: endpoint.baseImageUri,
sourceCode: {
cloudStorageSource: {
bucket: storageSource.bucket,
object: storageSource.object,
generation: storageSource.generation ? String(storageSource.generation) : undefined,
},
},
resources: {
limits: {
cpu: String(endpoint.cpu || 1),
memory: `${endpoint.availableMemoryMb || 256}Mi`,
},
cpuIdle: true,
startupCpuBoost: true,
},
},
],
maxInstanceRequestConcurrency: endpoint.concurrency || 80,
scaling: {
minInstanceCount: endpoint.minInstances || 0,
maxInstanceCount: endpoint.maxInstances || 100,
},
},
client: "cli-firebase",
labels: { ...endpoint.labels, "goog-managed-by": "firebase-functions" },
annotations: {},
};

await this.executor
.run(async () => {
const op = await runV2.createService(
endpoint.project,
endpoint.region,
endpoint.id,
service,
);
endpoint.uri = op.uri;
endpoint.runServiceId = endpoint.id;
})
.catch(rethrowAs(endpoint, "create"));

await this.setInvoker(endpoint);
}

async updateRunService(update: planner.EndpointUpdate): Promise<void> {
const endpoint = update.endpoint;
const storageSource = this.sources[endpoint.codebase!]?.storage;
if (!storageSource) {
logger.debug("Precondition failed. Cannot update a Cloud Run function without storage");
throw new Error("Precondition failed");
}

const service: Omit<runV2.Service, runV2.ServiceOutputFields> = {
name: `projects/${endpoint.project}/locations/${endpoint.region}/services/${endpoint.id}`,
template: {
containers: [
{
name: "worker",
image: "scratch",
command: endpoint.command,
args: endpoint.args,
baseImageUri: endpoint.baseImageUri,
sourceCode: {
cloudStorageSource: {
bucket: storageSource.bucket,
object: storageSource.object,
generation: storageSource.generation ? String(storageSource.generation) : undefined,
},
},
resources: {
limits: {
cpu: String(endpoint.cpu || 1),
memory: `${endpoint.availableMemoryMb || 256}Mi`,
},
cpuIdle: true,
startupCpuBoost: true,
},
},
],
maxInstanceRequestConcurrency: endpoint.concurrency || 80,
scaling: {
minInstanceCount: endpoint.minInstances || 0,
maxInstanceCount: endpoint.maxInstances || 100,
},
},
client: "cli-firebase",
labels: { ...endpoint.labels, "goog-managed-by": "firebase-functions" },
annotations: {},
};

await this.executor
.run(async () => {
const op = await runV2.updateService(service);
endpoint.uri = op.uri;
endpoint.runServiceId = endpoint.id;
})
.catch(rethrowAs(endpoint, "update"));

await this.setInvoker(endpoint);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The methods createRunService and updateRunService share a large block of code for constructing the service object. This duplication can be avoided by extracting the common logic into a private helper method. This will improve maintainability and reduce redundancy.

For example, you could create a helper method like this:

private _runServiceFromEndpoint(endpoint: backend.Endpoint, storageSource: gcfV2.StorageSource): Omit<runV2.Service, runV2.ServiceOutputFields> {
  return {
    name: `projects/${endpoint.project}/locations/${endpoint.region}/services/${endpoint.id}`,
    template: {
      containers: [
        {
          name: "worker",
          image: "scratch",
          command: endpoint.command,
          args: endpoint.args,
          baseImageUri: endpoint.baseImageUri,
          sourceCode: {
            cloudStorageSource: {
              bucket: storageSource.bucket,
              object: storageSource.object,
              generation: storageSource.generation ? String(storageSource.generation) : undefined,
            },
          },
          resources: {
            limits: {
              cpu: String(endpoint.cpu || 1),
              memory: `${endpoint.availableMemoryMb || 256}Mi`,
            },
            cpuIdle: true,
            startupCpuBoost: true,
          },
        },
      ],
      maxInstanceRequestConcurrency: endpoint.concurrency || 80,
      scaling: {
        minInstanceCount: endpoint.minInstances || 0,
        maxInstanceCount: endpoint.maxInstances || 100,
      },
    },
    client: "cli-firebase",
    labels: { ...endpoint.labels, "goog-managed-by": "firebase-functions" },
    annotations: {},
  };
}

Then, createRunService and updateRunService can be simplified to use this helper.

Comment on lines +22 to +27
// @ts-ignore
const result = buildFromV1Alpha1(yaml, "danielylee-91", "us-west1", "dart");
console.log("Result endpoints:", JSON.stringify(result.endpoints, null, 2));

// @ts-ignore
const endpoint = (result.endpoints as any)["darttest"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of @ts-ignore and as any can be avoided here with proper typing. This will make the verification script cleaner and more type-safe.

Suggested change
// @ts-ignore
const result = buildFromV1Alpha1(yaml, "danielylee-91", "us-west1", "dart");
console.log("Result endpoints:", JSON.stringify(result.endpoints, null, 2));
// @ts-ignore
const endpoint = (result.endpoints as any)["darttest"];
const result = buildFromV1Alpha1(yaml as unknown, "danielylee-91", "us-west1", "dart");
console.log("Result endpoints:", JSON.stringify(result.endpoints, null, 2));
const endpoint = result.endpoints["darttest"];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant